/*
 * Copyright 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

@file:OptIn(InternalComposeApi::class)
@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")

package androidx.compose.runtime

import androidx.compose.runtime.collection.ScopeMap
import androidx.compose.runtime.tooling.ComposeStackTraceFrame
import androidx.compose.runtime.tooling.CompositionData
import androidx.compose.runtime.tooling.CompositionErrorContextImpl
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
import kotlin.coroutines.CoroutineContext
import kotlin.jvm.JvmInline

/**
 * Composer is the interface that is targeted by the Compose Kotlin compiler plugin and used by code
 * generation helpers. It is highly recommended that direct calls these be avoided as the runtime
 * assumes that the calls are generated by the compiler and contain only a minimum amount of state
 * validation.
 */
public sealed interface Composer {
    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Changes calculated and recorded during composition and are sent to [applier] which makes the
     * physical changes to the node tree implied by a composition.
     *
     * Composition has two discrete phases, 1) calculate and record changes and 2) making the
     * changes via the [applier]. While a [Composable] functions is executing, none of the [applier]
     * methods are called. The recorded changes are sent to the [applier] all at once after all
     * [Composable] functions have completed.
     */
    @ComposeCompilerApi public val applier: Applier<*>

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Reflects that a new part of the composition is being created, that is, the composition will
     * insert new nodes into the resulting tree.
     */
    @ComposeCompilerApi public val inserting: Boolean

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Reflects whether the [Composable] function can skip. Even if a [Composable] function is
     * called with the same parameters it might still need to run because, for example, a new value
     * was provided for a [CompositionLocal] created by [staticCompositionLocalOf].
     */
    @ComposeCompilerApi public val skipping: Boolean

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Reflects whether the default parameter block of a [Composable] function is valid. This is
     * `false` if a [State] object read in the [startDefaults] group was modified since the last
     * time the [Composable] function was run.
     */
    @ComposeCompilerApi public val defaultsInvalid: Boolean

    /**
     * A Compose internal property. DO NOT call directly. Use [currentRecomposeScope] instead.
     *
     * The invalidation current invalidation scope. An new invalidation scope is created whenever
     * [startRestartGroup] is called. when this scope's [RecomposeScope.invalidate] is called then
     * lambda supplied to [endRestartGroup]'s [ScopeUpdateScope] will be scheduled to be run.
     */
    @InternalComposeApi public val recomposeScope: RecomposeScope?

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Return an object that can be used to uniquely identity of the current recomposition scope.
     * This identity will be the same even if the recompose scope instance changes.
     *
     * This is used internally by tooling track composable function invocations.
     */
    @ComposeCompilerApi public val recomposeScopeIdentity: Any?

    /**
     * A Compose internal property. DO NOT call directly. Use [currentCompositeKeyHash] instead.
     *
     * This a hash value used to map externally stored state to the composition. For example, this
     * is used by saved instance state to preserve state across activity lifetime boundaries.
     *
     * This value is likely but not guaranteed to be unique. There are known cases, such as for
     * loops without a unique [key], where the runtime does not have enough information to make the
     * compound key hash unique.
     */
    @Deprecated(
        "Prefer the higher-precision compositeKeyHashCode instead",
        ReplaceWith("compositeKeyHashCode"),
    )
    @InternalComposeApi
    public val compoundKeyHash: Int
        get() = compositeKeyHashCode.hashCode()

    /**
     * A Compose internal property. DO NOT call directly. Use [currentCompositeKeyHashCode] instead.
     *
     * This a hash value used to map externally stored state to the composition. For example, this
     * is used by saved instance state to preserve state across activity lifetime boundaries.
     *
     * This value is likely but not guaranteed to be unique. There are known cases, such as for
     * loops without a unique [key], where the runtime does not have enough information to make the
     * compound key hash unique.
     */
    @InternalComposeApi public val compositeKeyHashCode: CompositeKeyHashCode

    // Groups

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Start a replaceable group. A replaceable group is a group that cannot be moved during
     * execution and can only either inserted, removed, or replaced. For example, the group created
     * by most control flow constructs such as an `if` statement are replaceable groups.
     *
     * Warning: Versions of the compiler that generate calls to this function also contain subtle
     * bug that does not generate a group around a loop containing code that just creates composable
     * lambdas (AnimatedContent from androidx.compose.animation, for example) which makes replacing
     * the group unsafe and the this must treat this like a movable group. [startReplaceGroup] was
     * added that will replace the group as described above and is only called by versions of the
     * compiler that correctly generate code around loops that create lambdas. This method is kept
     * to maintain compatibility with code generated by older versions of the compose compiler
     * plugin.
     *
     * @param key A compiler generated key based on the source location of the call.
     */
    @ComposeCompilerApi public fun startReplaceableGroup(key: Int)

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Called at the end of a replaceable group.
     *
     * @see startRestartGroup
     */
    @ComposeCompilerApi public fun endReplaceableGroup()

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Start a replace group. A replace group is a group that cannot be moved during must only
     * either be inserted, removed, or replaced. For example, the group created by most control flow
     * constructs such as an `if` statement are replaceable groups.
     *
     * Note: This method replaces [startReplaceableGroup] which is only generated by older versions
     * of the compose compiler plugin that predate the addition of this method. The runtime is now
     * required to replace the group if a different group is detected instead of treating it like a
     * movable group.
     *
     * @param key A compiler generated key based on the source location of the call.
     * @see endReplaceGroup
     */
    @ComposeCompilerApi public fun startReplaceGroup(key: Int)

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Called at the end of a replace group.
     *
     * @see startReplaceGroup
     */
    @ComposeCompilerApi public fun endReplaceGroup()

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Start a movable group. A movable group is one that can be moved based on the value of
     * [dataKey] which is typically supplied by the [key][androidx.compose.runtime.key] pseudo
     * compiler function.
     *
     * A movable group implements the semantics of [key][androidx.compose.runtime.key] which allows
     * the state and nodes generated by a loop to move with the composition implied by the key
     * passed to [key][androidx.compose.runtime.key].
     *
     * @param key a compiler generated key based on the source location of the call.
     * @param dataKey an additional object that is used as a second part of the key. This key
     *   produced from the `keys` parameter supplied to the [key][androidx.compose.runtime.key]
     *   pseudo compiler function.
     */
    @ComposeCompilerApi public fun startMovableGroup(key: Int, dataKey: Any?)

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Called at the end of a movable group.
     *
     * @see startMovableGroup
     */
    @ComposeCompilerApi public fun endMovableGroup()

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Called to start the group that calculates the default parameters of a [Composable] function.
     *
     * This method is called near the beginning of a [Composable] function with default parameters
     * and surrounds the remembered values or [Composable] calls necessary to produce the default
     * parameters. For example, for `model: Model = remember { DefaultModel() }` the call to
     * [remember] is called inside a [startDefaults] group.
     */
    @ComposeCompilerApi public fun startDefaults()

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Called at the end of defaults group.
     *
     * @see startDefaults
     */
    @ComposeCompilerApi public fun endDefaults()

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Called to record a group for a [Composable] function and starts a group that can be
     * recomposed on demand based on the lambda passed to
     * [updateScope][ScopeUpdateScope.updateScope] when [endRestartGroup] is called
     *
     * @param key A compiler generated key based on the source location of the call.
     * @return the instance of the composer to use for the rest of the function.
     */
    @ComposeCompilerApi public fun startRestartGroup(key: Int): Composer

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Called to end a restart group.
     */
    @ComposeCompilerApi public fun endRestartGroup(): ScopeUpdateScope?

    /**
     * A Compose internal API. DO NOT call directly.
     *
     * Request movable content be inserted at the current location. This will schedule with the root
     * composition parent a call to [insertMovableContent] with the correct [MovableContentState] if
     * one was released in another part of composition.
     */
    @InternalComposeApi public fun insertMovableContent(value: MovableContent<*>, parameter: Any?)

    /**
     * A Compose internal API. DO NOT call directly.
     *
     * Perform a late composition that adds to the current late apply that will insert the given
     * references to [MovableContent] into the composition. If a [MovableContent] is paired then
     * this is a request to move a released [MovableContent] from a different location or from a
     * different composition. If it is not paired (i.e. the `second` [MovableContentStateReference]
     * is `null`) then new state for the [MovableContent] is inserted into the composition.
     */
    @InternalComposeApi
    public fun insertMovableContentReferences(
        references: List<Pair<MovableContentStateReference, MovableContentStateReference?>>
    )

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Record the source information string for a group. This must be immediately called after the
     * start of a group.
     *
     * @param sourceInformation An string value to that provides the compose tools enough
     *   information to calculate the source location of calls to composable functions.
     */
    public fun sourceInformation(sourceInformation: String)

    /**
     * A compose compiler plugin API. DO NOT call directly.
     *
     * Record a source information marker. This marker can be used in place of a group that would
     * have contained the information but was elided as the compiler plugin determined the group was
     * not necessary such as when a function is marked with [ReadOnlyComposable].
     *
     * @param key A compiler generated key based on the source location of the call.
     * @param sourceInformation An string value to that provides the compose tools enough
     *   information to calculate the source location of calls to composable functions.
     */
    public fun sourceInformationMarkerStart(key: Int, sourceInformation: String)

    /**
     * A compose compiler plugin API. DO NOT call directly.
     *
     * Record the end of the marked source information range.
     */
    public fun sourceInformationMarkerEnd()

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Skips the composer to the end of the current group. This generated by the compiler to when
     * the body of a [Composable] function can be skipped typically because the parameters to the
     * function are equal to the values passed to it in the previous composition.
     */
    @ComposeCompilerApi public fun skipToGroupEnd()

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Deactivates the content to the end of the group by treating content as if it was deleted and
     * replaces all slot table entries for calls to [cache] to be [Empty]. This must be called as
     * the first call for a group.
     */
    @ComposeCompilerApi public fun deactivateToEndGroup(changed: Boolean)

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Skips the current group. This called by the compiler to indicate that the current group can
     * be skipped, for example, this is generated to skip the [startDefaults] group the default
     * group is was not invalidated.
     */
    @ComposeCompilerApi public fun skipCurrentGroup()

    // Nodes

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Start a group that tracks a the code that will create or update a node that is generated as
     * part of the tree implied by the composition.
     */
    @ComposeCompilerApi public fun startNode()

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Start a group that tracks a the code that will create or update a node that is generated as
     * part of the tree implied by the composition. A reusable node can be reused in a reusable
     * group even if the group key is changed.
     */
    @ComposeCompilerApi public fun startReusableNode()

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Report the [factory] that will be used to create the node that will be generated into the
     * tree implied by the composition. This will only be called if [inserting] is is `true`.
     *
     * @param factory a factory function that will generate a node that will eventually be supplied
     *   to [applier] though [Applier.insertBottomUp] and [Applier.insertTopDown].
     */
    @ComposeCompilerApi public fun <T> createNode(factory: () -> T)

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Report that the node is still being used. This will be called in the same location as the
     * corresponding [createNode] when [inserting] is `false`.
     */
    @ComposeCompilerApi public fun useNode()

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Called at the end of a node group.
     */
    @ComposeCompilerApi public fun endNode()

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Start a reuse group. Unlike a movable group, in a reuse group if the [dataKey] changes the
     * composition shifts into a reusing state cause the composer to act like it is inserting (e.g.
     * [cache] acts as if all values are invalid, [changed] always returns true, etc.) even though
     * it is recomposing until it encounters a reusable node. If the node is reusable it temporarily
     * shifts into recomposition for the node and then shifts back to reusing for the children. If a
     * non-reusable node is generated the composer shifts to inserting for the node and all of its
     * children.
     *
     * @param key An compiler generated key based on the source location of the call.
     * @param dataKey A key provided by the [ReusableContent] composable function that is used to
     *   determine if the composition shifts into a reusing state for this group.
     */
    @ComposeCompilerApi public fun startReusableGroup(key: Int, dataKey: Any?)

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Called at the end of a reusable group.
     */
    @ComposeCompilerApi public fun endReusableGroup()

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Temporarily disable reusing if it is enabled.
     */
    @ComposeCompilerApi public fun disableReusing()

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Reenable reusing if it was previously enabled before the last call to [disableReusing].
     */
    @ComposeCompilerApi public fun enableReusing()

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Return a marker for the current group that can be used in a call to [endToMarker].
     */
    @ComposeCompilerApi public val currentMarker: Int

    /**
     * Compose compiler plugin API. DO NOT call directly.
     *
     * Ends all the groups up to but not including the group that is the parent group when
     * [currentMarker] was called to produce [marker]. All groups ended must have been started with
     * either [startReplaceableGroup] or [startMovableGroup]. Ending other groups can cause the
     * state of the composer to become inconsistent.
     */
    @ComposeCompilerApi public fun endToMarker(marker: Int)

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Schedule [block] to called with [value]. This is intended to update the node generated by
     * [createNode] to changes discovered by composition.
     *
     * @param value the new value to be set into some property of the node.
     * @param block the block that sets the some property of the node to [value].
     */
    @ComposeCompilerApi public fun <V, T> apply(value: V, block: T.(V) -> Unit)

    // State

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Produce an object that will compare equal an iff [left] and [right] compare equal to some
     * [left] and [right] of a previous call to [joinKey]. This is used by [key] to handle multiple
     * parameters. Since the previous composition stored [left] and [right] in a "join key" object
     * this call is used to return the previous value without an allocation instead of blindly
     * creating a new value that will be immediately discarded.
     *
     * @param left the first part of a a joined key.
     * @param right the second part of a joined key.
     * @return an object that will compare equal to a value previously returned by [joinKey] iff
     *   [left] and [right] compare equal to the [left] and [right] passed to the previous call.
     */
    @ComposeCompilerApi public fun joinKey(left: Any?, right: Any?): Any

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Remember a value into the composition state. This is a primitive method used to implement
     * [remember].
     *
     * @return [Composer.Empty] when [inserting] is `true` or the value passed to
     *   [updateRememberedValue] from the previous composition.
     * @see cache
     */
    @ComposeCompilerApi public fun rememberedValue(): Any?

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Update the remembered value correspond to the previous call to [rememberedValue]. The [value]
     * will be returned by [rememberedValue] for the next composition.
     */
    @ComposeCompilerApi public fun updateRememberedValue(value: Any?)

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Check [value] is different than the value used in the previous composition. This is used, for
     * example, to check parameter values to determine if they have changed.
     *
     * @param value the value to check
     * @return `true` if the value if [equals] of the previous value returns `false` when passed
     *   [value].
     */
    @ComposeCompilerApi public fun changed(value: Any?): Boolean

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Check [value] is different than the value used in the previous composition. This is used, for
     * example, to check parameter values to determine if they have changed.
     *
     * This overload is provided to avoid boxing [value] to compare with a potentially boxed version
     * of [value] in the composition state.
     *
     * @param value the value to check
     * @return `true` if the value if [equals] of the previous value returns `false` when passed
     *   [value].
     */
    @ComposeCompilerApi public fun changed(value: Boolean): Boolean = changed(value)

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Check [value] is different than the value used in the previous composition. This is used, for
     * example, to check parameter values to determine if they have changed.
     *
     * This overload is provided to avoid boxing [value] to compare with a potentially boxed version
     * of [value] in the composition state.
     *
     * @param value the value to check
     * @return `true` if the value if [equals] of the previous value returns `false` when passed
     *   [value].
     */
    @ComposeCompilerApi public fun changed(value: Char): Boolean = changed(value)

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Check [value] is different than the value used in the previous composition. This is used, for
     * example, to check parameter values to determine if they have changed.
     *
     * This overload is provided to avoid boxing [value] to compare with a potentially boxed version
     * of [value] in the composition state.
     *
     * @param value the value to check
     * @return `true` if the value if [equals] of the previous value returns `false` when passed
     *   [value].
     */
    @ComposeCompilerApi public fun changed(value: Byte): Boolean = changed(value)

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Check [value] is different than the value used in the previous composition. This is used, for
     * example, to check parameter values to determine if they have changed.
     *
     * This overload is provided to avoid boxing [value] to compare with a potentially boxed version
     * of [value] in the composition state.
     *
     * @param value the value to check
     * @return `true` if the value if [equals] of the previous value returns `false` when passed
     *   [value].
     */
    @ComposeCompilerApi public fun changed(value: Short): Boolean = changed(value)

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Check [value] is different than the value used in the previous composition. This is used, for
     * example, to check parameter values to determine if they have changed.
     *
     * This overload is provided to avoid boxing [value] to compare with a potentially boxed version
     * of [value] in the composition state.
     *
     * @param value the value to check
     * @return `true` if the value if [equals] of the previous value returns `false` when passed
     *   [value].
     */
    @ComposeCompilerApi public fun changed(value: Int): Boolean = changed(value)

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Check [value] is different than the value used in the previous composition. This is used, for
     * example, to check parameter values to determine if they have changed.
     *
     * This overload is provided to avoid boxing [value] to compare with a potentially boxed version
     * of [value] in the composition state.
     *
     * @param value the value to check
     * @return `true` if the value if [equals] of the previous value returns `false` when passed
     *   [value].
     */
    @ComposeCompilerApi public fun changed(value: Float): Boolean = changed(value)

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Check [value] is different than the value used in the previous composition. This is used, for
     * example, to check parameter values to determine if they have changed.
     *
     * This overload is provided to avoid boxing [value] to compare with a potentially boxed version
     * of [value] in the composition state.
     *
     * @param value the value to check
     * @return `true` if the value if [equals] of the previous value returns `false` when passed
     *   [value].
     */
    @ComposeCompilerApi public fun changed(value: Long): Boolean = changed(value)

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Check [value] is different than the value used in the previous composition. This is used, for
     * example, to check parameter values to determine if they have changed.
     *
     * This overload is provided to avoid boxing [value] to compare with a potentially boxed version
     * of [value] in the composition state.
     *
     * @param value the value to check
     * @return `true` if the value if [equals] of the previous value returns `false` when passed
     *   [value].
     */
    @ComposeCompilerApi public fun changed(value: Double): Boolean = changed(value)

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Check [value] is different than the value used in the previous composition using `===`
     * instead of `==` equality. This is used, for example, to check parameter values to determine
     * if they have changed for values that use value equality but, for correct behavior, the
     * composer needs reference equality.
     *
     * @param value the value to check
     * @return `true` if the value is === equal to the previous value and returns `false` when
     *   [value] is different.
     */
    @ComposeCompilerApi public fun changedInstance(value: Any?): Boolean = changed(value)

    // Scopes

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Mark [scope] as used. [endReplaceableGroup] will return `null` unless [recordUsed] is called
     * on the corresponding [scope]. This is called implicitly when [State] objects are read during
     * composition is called when [currentRecomposeScope] is called in the [Composable] function.
     */
    @InternalComposeApi public fun recordUsed(scope: RecomposeScope)

    /**
     * A Compose compiler plugin API. DO NOT call directly.
     *
     * Generated by the compile to determine if the composable function should be executed. It may
     * not execute if parameter has not changed and the nothing else is forcing the function to
     * execute (such as its scope was invalidated or a static composition local it was changed) or
     * the composition is pausable and the composition is pausing.
     *
     * @param parametersChanged `true` if the parameters to the composable function have changed.
     *   This is also `true` if the composition is [inserting] or if content is being reused.
     * @param flags The `$changed` parameter that contains the forced recompose bit to allow the
     *   composer to disambiguate when the parameters changed due the execution being forced or if
     *   the parameters actually changed. This is only ambiguous in a [PausableComposition] and is
     *   necessary to determine if the function can be paused. The bits, other than 0, are reserved
     *   for future use (which would required the bit 31, which is unused in `$changed` values, to
     *   be set to indicate that the flags carry additional information). Passing the `$changed`
     *   flags directly, instead of masking the 0 bit, is more efficient as it allows less code to
     *   be generated per call to `shouldExecute` which is every called in every restartable
     *   function, as well as allowing for the API to be extended without a breaking changed.
     */
    @InternalComposeApi public fun shouldExecute(parametersChanged: Boolean, flags: Int): Boolean

    // Internal API

    /**
     * A Compose internal function. DO NOT call directly.
     *
     * Record a function to call when changes to the corresponding tree are applied to the
     * [applier]. This is used to implement [SideEffect].
     *
     * @param effect a lambda to invoke after the changes calculated up to this point have been
     *   applied.
     */
    @InternalComposeApi public fun recordSideEffect(effect: () -> Unit)

    /**
     * Returns the active set of CompositionLocals at the current position in the composition
     * hierarchy. This is a lower level API that can be used to export and access CompositionLocal
     * values outside of Composition.
     *
     * This API does not track reads of CompositionLocals and does not automatically dispatch new
     * values to previous readers when the value of a CompositionLocal changes. To use this API as
     * intended, you must set up observation manually. This means:
     * - For [non-static CompositionLocals][compositionLocalOf], composables reading this map need
     *   to observe the snapshot state for CompositionLocals being read to be notified when their
     *   values in this map change.
     * - For [static CompositionLocals][staticCompositionLocalOf], all composables including the
     *   composable reading this map will be recomposed and you will need to re-obtain this map to
     *   get the latest values.
     *
     * Most applications shouldn't use this API directly, and should instead use
     * [CompositionLocal.current].
     */
    public val currentCompositionLocalMap: CompositionLocalMap

    /**
     * A Compose internal function. DO NOT call directly.
     *
     * Return the [CompositionLocal] value associated with [key]. This is the primitive function
     * used to implement [CompositionLocal.current].
     *
     * @param key the [CompositionLocal] value to be retrieved.
     */
    @InternalComposeApi public fun <T> consume(key: CompositionLocal<T>): T

    /**
     * A Compose internal function. DO NOT call directly.
     *
     * Provide the given values for the associated [CompositionLocal] keys. This is the primitive
     * function used to implement [CompositionLocalProvider].
     *
     * @param values an array of value to provider key pairs.
     */
    @InternalComposeApi public fun startProviders(values: Array<out ProvidedValue<*>>)

    /**
     * A Compose internal function. DO NOT call directly.
     *
     * End the provider group.
     *
     * @see startProviders
     */
    @InternalComposeApi public fun endProviders()

    /**
     * A Compose internal function. DO NOT call directly.
     *
     * Provide the given value for the associated [CompositionLocal] key. This is the primitive
     * function used to implement [CompositionLocalProvider].
     *
     * @param value a value to provider key pairs.
     */
    @InternalComposeApi public fun startProvider(value: ProvidedValue<*>)

    /**
     * A Compose internal function. DO NOT call directly.
     *
     * End the provider group.
     *
     * @see startProvider
     */
    @InternalComposeApi public fun endProvider()

    /**
     * A tooling API function. DO NOT call directly.
     *
     * The data stored for the composition. This is used by Compose tools, such as the preview and
     * the inspector, to display or interpret the result of composition.
     */
    public val compositionData: CompositionData

    /**
     * A tooling API function. DO NOT call directly.
     *
     * Called by the inspector to inform the composer that it should collect additional information
     * about call parameters. By default, only collect parameter information for scopes that are
     * [recordUsed] has been called on. If [collectParameterInformation] is called it will attempt
     * to collect all calls even if the runtime doesn't need them.
     *
     * WARNING: calling this will result in a significant number of additional allocations that are
     * typically avoided.
     */
    public fun collectParameterInformation()

    /**
     * Schedules an [action] to be invoked when the recomposer finishes the next execution of a
     * frame. If a frame is currently in-progress, [action] will be invoked when the current frame
     * finishes. If a frame isn't currently in-progress, a new frame will be scheduled (if one
     * hasn't been already) and [action] will execute at the completion of the next frame.
     *
     * [action] will always execute on the applier thread.
     *
     * Note that [action] runs at the end of a frame scheduled by the recomposer. If a callback is
     * scheduled via this method during the initial composition, it will not execute until the
     * _next_ frame.
     *
     * @return A [CancellationHandle] that can be used to unregister the [action]. The returned
     *   handle is thread-safe and may be cancelled from any thread. Cancelling the handle only
     *   removes the callback from the queue. If [action] is currently executing, it will not be
     *   cancelled by this handle.
     */
    public fun scheduleFrameEndCallback(action: () -> Unit): CancellationHandle

    /**
     * A Compose internal function. DO NOT call directly.
     *
     * Build a composition context that can be used to created a subcomposition. A composition
     * reference is used to communicate information from this composition to the subcompositions
     * such as the all the [CompositionLocal]s provided at the point the reference is created.
     */
    @InternalComposeApi public fun buildContext(): CompositionContext

    /**
     * A Compose internal function. DO NOT call directly.
     *
     * The coroutine context for the composition. This is used, for example, to implement
     * [LaunchedEffect]. This context is managed by the [Recomposer].
     */
    @InternalComposeApi
    public val applyCoroutineContext: CoroutineContext
        @TestOnly get

    /** The composition that is used to control this composer. */
    public val composition: ControlledComposition
        @TestOnly get

    /**
     * Disable the collection of source information, that may introduce groups to store the source
     * information, in order to be able to more accurately calculate the actual number of groups a
     * composable function generates in a release build.
     *
     * This function is only safe to call in a test and will produce incorrect composition results
     * if called on a composer not under test.
     */
    @TestOnly public fun disableSourceInformation()

    public companion object {
        /**
         * A special value used to represent no value was stored (e.g. an empty slot). This is
         * returned, for example by [Composer.rememberedValue] while it is [Composer.inserting] is
         * `true`.
         */
        public val Empty: Any =
            object {
                override fun toString() = "Empty"
            }

        /**
         * Internal API for specifying a tracer used for instrumenting frequent operations, e.g.
         * recompositions.
         */
        @InternalComposeTracingApi
        public fun setTracer(tracer: CompositionTracer?) {
            compositionTracer = tracer
        }

        /**
         * Enable composition stack traces based on the source information. When this flag is
         * enabled, composition will record source information at runtime. When crash occurs,
         * Compose will append a suppressed exception that contains a stack trace pointing to the
         * place in composition closest to the crash.
         *
         * Note that:
         * - Recording source information introduces additional performance overhead, so this option
         *   should NOT be enabled in release builds.
         * - Compose ships with a minifier config that removes source information from the release
         *   builds. Enabling this flag in minified builds will have no effect.
         */
        @ExperimentalComposeRuntimeApi
        public fun setDiagnosticStackTraceEnabled(enabled: Boolean) {
            composeStackTraceEnabled = enabled
        }
    }
}

/**
 * A Compose compiler plugin API. DO NOT call directly.
 *
 * Cache, that is remember, a value in the composition data of a composition. This is used to
 * implement [remember] and used by the compiler plugin to generate more efficient calls to
 * [remember] when it determines these optimizations are safe.
 */
@ComposeCompilerApi
public inline fun <T> Composer.cache(invalid: Boolean, block: @DisallowComposableCalls () -> T): T {
    @Suppress("UNCHECKED_CAST")
    return rememberedValue().let {
        if (invalid || it === Composer.Empty) {
            val value = block()
            updateRememberedValue(value)
            value
        } else it
    } as T
}

/**
 * A Compose internal function. DO NOT call directly.
 *
 * Records source information that can be used for tooling to determine the source location of the
 * corresponding composable function. By default, this function is declared as having no
 * side-effects. It is safe for code shrinking tools (such as R8 or ProGuard) to remove it.
 */
@ComposeCompilerApi
public fun sourceInformation(composer: Composer, sourceInformation: String) {
    composer.sourceInformation(sourceInformation)
}

/**
 * A Compose internal function. DO NOT call directly.
 *
 * Records the start of a source information marker that can be used for tooling to determine the
 * source location of the corresponding composable function that otherwise don't require tracking
 * information such as [ReadOnlyComposable] functions. By default, this function is declared as
 * having no side-effects. It is safe for code shrinking tools (such as R8 or ProGuard) to remove
 * it.
 *
 * Important that both [sourceInformationMarkerStart] and [sourceInformationMarkerEnd] are removed
 * together or both kept. Removing only one will cause incorrect runtime behavior.
 */
@ComposeCompilerApi
public fun sourceInformationMarkerStart(composer: Composer, key: Int, sourceInformation: String) {
    composer.sourceInformationMarkerStart(key, sourceInformation)
}

/**
 * Internal tracing API.
 *
 * Should be called without thread synchronization with occasional information loss.
 */
@InternalComposeTracingApi
public interface CompositionTracer {
    public fun traceEventStart(key: Int, dirty1: Int, dirty2: Int, info: String): Unit

    public fun traceEventEnd(): Unit

    public fun isTraceInProgress(): Boolean
}

@OptIn(InternalComposeTracingApi::class) private var compositionTracer: CompositionTracer? = null

internal var composeStackTraceEnabled: Boolean = false

/**
 * Internal tracing API.
 *
 * Should be called without thread synchronization with occasional information loss.
 */
@OptIn(InternalComposeTracingApi::class)
@ComposeCompilerApi
public fun isTraceInProgress(): Boolean =
    compositionTracer.let { it != null && it.isTraceInProgress() }

@OptIn(InternalComposeTracingApi::class)
@ComposeCompilerApi
@Deprecated(
    message = "Use the overload with \$dirty metadata instead",
    ReplaceWith("traceEventStart(key, dirty1, dirty2, info)"),
    DeprecationLevel.HIDDEN,
)
public fun traceEventStart(key: Int, info: String): Unit = traceEventStart(key, -1, -1, info)

/**
 * Internal tracing API.
 *
 * Should be called without thread synchronization with occasional information loss.
 *
 * @param key is a group key generated by the compiler plugin for the function being traced. This
 *   key is unique the function.
 * @param dirty1 $dirty metadata: forced-recomposition and function parameters 1..10 if present
 * @param dirty2 $dirty2 metadata: forced-recomposition and function parameters 11..20 if present
 * @param info is a user displayable string that describes the function for which this is the start
 *   event.
 */
@OptIn(InternalComposeTracingApi::class)
@ComposeCompilerApi
public fun traceEventStart(key: Int, dirty1: Int, dirty2: Int, info: String) {
    compositionTracer?.traceEventStart(key, dirty1, dirty2, info)
}

/**
 * Internal tracing API.
 *
 * Should be called without thread synchronization with occasional information loss.
 */
@OptIn(InternalComposeTracingApi::class)
@ComposeCompilerApi
public fun traceEventEnd() {
    compositionTracer?.traceEventEnd()
}

/**
 * A Compose internal function. DO NOT call directly.
 *
 * Records the end of a source information marker that can be used for tooling to determine the
 * source location of the corresponding composable function that otherwise don't require tracking
 * information such as [ReadOnlyComposable] functions. By default, this function is declared as
 * having no side-effects. It is safe for code shrinking tools (such as R8 or ProGuard) to remove
 * it.
 *
 * Important that both [sourceInformationMarkerStart] and [sourceInformationMarkerEnd] are removed
 * together or both kept. Removing only one will cause incorrect runtime behavior.
 */
@ComposeCompilerApi
public fun sourceInformationMarkerEnd(composer: Composer) {
    composer.sourceInformationMarkerEnd()
}

/** The internal calls the Composition makes to a Composer */
internal sealed interface InternalComposer : Composer {
    val areChildrenComposing: Boolean
    val currentRecomposeScope: RecomposeScopeImpl?
    var deferredChanges: Changes?
    val errorContext: CompositionErrorContextImpl?
    val hasPendingChanges: Boolean
    val isComposing: Boolean

    fun composeContent(
        invalidationsRequested: ScopeMap<RecomposeScopeImpl, Any>,
        content: @Composable () -> Unit,
        shouldPause: ShouldPauseCallback?,
    )

    fun changesApplied()

    fun deactivate()

    fun dispose()

    fun endReuseFromRoot()

    fun forceRecomposeScopes(): Boolean

    fun parentStackTrace(): List<ComposeStackTraceFrame>

    fun prepareCompose(block: () -> Unit)

    /**
     * Synchronously recompose all invalidated groups. This collects the changes which must be
     * applied by [ControlledComposition.applyChanges] to have an effect.
     */
    fun recompose(
        invalidationsRequested: ScopeMap<RecomposeScopeImpl, Any>,
        shouldPause: ShouldPauseCallback?,
    ): Boolean

    @TestOnly fun stacksSize(): Int

    fun stackTraceForValue(value: Any?): List<ComposeStackTraceFrame>

    fun startReuseFromRoot()

    fun tryImminentInvalidation(scope: RecomposeScopeImpl, instance: Any?): Boolean

    fun updateComposerInvalidations(invalidationsRequested: ScopeMap<RecomposeScopeImpl, Any>)

    @TestOnly fun verifyConsistent()

    @TestOnly fun parentKey(): Int

    @TestOnly fun getInsertStorage(): SlotStorage
}

internal interface Reference<T> {
    val ref: T
}

/**
 * A helper receiver scope class used by [ComposeNode] to help write code to initialized and update
 * a node.
 *
 * @see ComposeNode
 */
@JvmInline
public value class Updater<T>(@PublishedApi internal val composer: Composer) {
    /**
     * Set the value property of the emitted node.
     *
     * Schedules [block] to be run when the node is first created or when [value] is different than
     * the previous composition.
     *
     * @see update
     */
    @Suppress("NOTHING_TO_INLINE") // Inlining the compare has noticeable impact
    public inline fun set(value: Int, noinline block: T.(value: Int) -> Unit): Unit =
        with(composer) {
            if (inserting || rememberedValue() != value) {
                updateRememberedValue(value)
                composer.apply(value, block)
            }
        }

    /**
     * Set the value property of the emitted node.
     *
     * Schedules [block] to be run when the node is first created or when [value] is different than
     * the previous composition.
     *
     * @see update
     */
    public fun <V> set(value: V, block: T.(value: V) -> Unit): Unit =
        with(composer) {
            if (inserting || rememberedValue() != value) {
                updateRememberedValue(value)
                composer.apply(value, block)
            }
        }

    /**
     * Update the value of a property of the emitted node.
     *
     * Schedules [block] to be run when [value] is different than the previous composition. It is
     * different than [set] in that it does not run when the node is created. This is used when
     * initial value set by the [ComposeNode] in the constructor callback already has the correct
     * value. For example, use [update} when [value] is passed into of the classes constructor
     * parameters.
     *
     * @see set
     */
    @Suppress("NOTHING_TO_INLINE") // Inlining the compare has noticeable impact
    public inline fun update(value: Int, noinline block: T.(value: Int) -> Unit): Unit =
        with(composer) {
            val inserting = inserting
            if (inserting || rememberedValue() != value) {
                updateRememberedValue(value)
                if (!inserting) apply(value, block)
            }
        }

    /**
     * Update the value of a property of the emitted node.
     *
     * Schedules [block] to be run when [value] is different than the previous composition. It is
     * different than [set] in that it does not run when the node is created. This is used when
     * initial value set by the [ComposeNode] in the constructor callback already has the correct
     * value. For example, use [update} when [value] is passed into of the classes constructor
     * parameters.
     *
     * @see set
     */
    public fun <V> update(value: V, block: T.(value: V) -> Unit): Unit =
        with(composer) {
            val inserting = inserting
            if (inserting || rememberedValue() != value) {
                updateRememberedValue(value)
                if (!inserting) apply(value, block)
            }
        }

    /**
     * Initialize emitted node.
     *
     * Schedule [block] to be executed after the node is created.
     *
     * This is only executed once. The can be used to call a method or set a value on a node
     * instance that is required to be set after one or more other properties have been set.
     *
     * @see reconcile
     */
    public fun init(block: T.() -> Unit) {
        if (composer.inserting) composer.apply<Unit, T>(Unit) { block() }
    }

    /**
     * Reconcile the node to the current state.
     *
     * This is used when [set] and [update] are insufficient to update the state of the node based
     * on changes passed to the function calling [ComposeNode].
     *
     * Schedules [block] to execute. As this unconditionally schedules [block] to executed it might
     * be executed unnecessarily as no effort is taken to ensure it only executes when the values
     * [block] captures have changed. It is highly recommended that [set] and [update] be used
     * instead as they will only schedule their blocks to executed when the value passed to them has
     * changed.
     */
    @Suppress("MemberVisibilityCanBePrivate")
    public fun reconcile(block: T.() -> Unit) {
        composer.apply<Unit, T>(Unit) { this.block() }
    }
}

@JvmInline
public value class SkippableUpdater<T>(@PublishedApi internal val composer: Composer) {
    public inline fun update(block: Updater<T>.() -> Unit) {
        composer.startReplaceableGroup(0x1e65194f)
        Updater<T>(composer).block()
        composer.endReplaceableGroup()
    }
}

/*
 * Remember observer which is not removed during reuse/deactivate of the group.
 * It is used to preserve composition locals between group deactivation.
 */
internal class ReusableRememberObserverHolder(wrapped: RememberObserver, after: Anchor?) :
    RememberObserverHolder(wrapped, after)

internal open class RememberObserverHolder(var wrapped: RememberObserver, var after: Anchor?)

/*
 * Integer keys are arbitrary values in the biload range. The do not need to be unique as if
 * there is a chance they will collide with a compiler generated key they are paired with a
 * OpaqueKey to ensure they are unique.
 */

// rootKey doesn't need a corresponding OpaqueKey as it never has sibling nodes and will always
// a unique key.
internal const val rootKey = 100

// An arbitrary key value for a node.
internal const val nodeKey = 125

// An arbitrary key value that marks the default parameter group
internal const val defaultsKey = -127

@PublishedApi internal const val invocationKey: Int = 200

@PublishedApi internal val invocation: Any = OpaqueKey("provider")

@PublishedApi internal const val providerKey: Int = 201

@PublishedApi internal val provider: Any = OpaqueKey("provider")

@PublishedApi internal const val compositionLocalMapKey: Int = 202

@PublishedApi internal val compositionLocalMap: Any = OpaqueKey("compositionLocalMap")

@PublishedApi internal const val providerValuesKey: Int = 203

@PublishedApi internal val providerValues: Any = OpaqueKey("providerValues")

@PublishedApi internal const val providerMapsKey: Int = 204

@PublishedApi internal val providerMaps: Any = OpaqueKey("providers")

@PublishedApi internal const val referenceKey: Int = 206

@PublishedApi internal val reference: Any = OpaqueKey("reference")

@PublishedApi internal const val reuseKey: Int = 207

internal class ComposeRuntimeError(override val message: String) : IllegalStateException()

@Suppress("BanInlineOptIn")
@OptIn(ExperimentalContracts::class)
internal inline fun runtimeCheck(value: Boolean, lazyMessage: () -> String) {
    contract { returns() implies value }
    if (!value) {
        composeImmediateRuntimeError(lazyMessage())
    }
}

internal const val EnableDebugRuntimeChecks = false

/**
 * A variation of [composeRuntimeError] that gets stripped from R8-minified builds. Use this for
 * more expensive checks or assertions along a hotpath that, if failed, would still lead to an
 * application crash that could be traced back to this assertion if removed from the final program
 * binary.
 */
internal inline fun debugRuntimeCheck(value: Boolean, lazyMessage: () -> String) {
    @Suppress("SimplifyBooleanWithConstants") // Intended compile time dead-code elimination
    if (EnableDebugRuntimeChecks && !value) {
        composeImmediateRuntimeError(lazyMessage())
    }
}

internal inline fun debugRuntimeCheck(value: Boolean) = debugRuntimeCheck(value) { "Check failed" }

internal inline fun runtimeCheck(value: Boolean) = runtimeCheck(value) { "Check failed" }

internal fun composeRuntimeError(message: String): Nothing {
    throw ComposeRuntimeError(
        "Compose Runtime internal error. Unexpected or incorrect use of the Compose " +
            "internal runtime API ($message). Please report to Google or use " +
            "https://goo.gle/compose-feedback"
    )
}

// Unit variant of composeRuntimeError() so the call site doesn't add 3 extra
// instructions to throw a KotlinNothingValueException
internal fun composeImmediateRuntimeError(message: String) {
    throw ComposeRuntimeError(
        "Compose Runtime internal error. Unexpected or incorrect use of the Compose " +
            "internal runtime API ($message). Please report to Google or use " +
            "https://goo.gle/compose-feedback"
    )
}
